export interface IgnoreDom {
  // 必填 ".gaodeMap"
  dom: string,
  // 可选，写数字即可（px）300
  height: number,
  // 可选，写数字即可（px）300
  width: number,
  // 可选：回放程度，基于主元素缩放后的大小 1.2
  scale: number,
  // 可选，如果自定义缩放后文字大小不合适，可以在这里设置文字大小 26
  fontSize: number
}

export interface AutoScreenOptions {
  // 设计稿的宽度，默认是 1920
  designWidth?: number,
  // 设计稿的高度，默认是 929 ，如果项目以全屏展示，则可以设置为1080
  designHeight?: number,
  renderDom?: null | HTMLElement | string,
  // 渲染的dom，默认是 "#app"，必须使用id选择器
  el?: null | HTMLElement | string,
  // 是否监听resize事件，默认是 true
  resize?: boolean,
  // 忽略缩放的元素（该元素将反向缩放）, 传入 ignore 以使元素不被缩放
  // 如果反向缩放后的元素大小使结构发生变化，你还可以手动传入宽高、回放程度。
  ignore?: Array<IgnoreDom>,
  // 过渡时间，默认是 0.6
  transition?: string,
  delay?: number
}

/**
 * 屏幕缩放适配方案  代替rem vw
 */
export class AutoScreenAdaptor {
  currRenderDom: string;
  currelRectification: string;
  currelRectificationLevel: number;
  resizeListener: null | Function;
  // eslint-disable-next-line no-undef
  timer: null | NodeJS.Timeout;
  currScale: number;
  isAutofitRunnig: boolean;
  isElRectification: boolean;
  isSupportScale: boolean;
  constructor (options?: AutoScreenOptions) {
    this.currRenderDom = '';
    this.currelRectification = '';
    this.currelRectificationLevel = 1;
    this.resizeListener = null;
    this.timer = null;
    this.currScale = 1;
    this.isAutofitRunnig = false;
    this.isElRectification = false;
    this.isSupportScale = this.isScaleSupport();
    this.init(options);
  }

  getDefaultOptions () {
    return {
      designWidth: 1920,
      designHeight: 929,
      renderDom: '#app',
      el: '#app',
      resize: true,
      ignore: [],
      transition: '0.6',
      delay: 0
    };
  }

  init (options?: AutoScreenOptions) {
    const defaultOptions = this.getDefaultOptions();
    if (!options) {
      options = defaultOptions;
    }
    const {
      designWidth = defaultOptions.designWidth,
      designHeight = defaultOptions.designHeight,
      renderDom,
      el = defaultOptions.el,
      resize = defaultOptions.resize,
      ignore = defaultOptions.ignore,
      transition = defaultOptions.transition,
      delay = defaultOptions.delay
    } = options;
    // @ts-ignore
    this.currRenderDom = el || renderDom;
    let dom: HTMLElement | null = null;
    if (typeof el === 'string') {
      dom = document.querySelector(el);
    }
    if (!dom) {
      console.error(`AutoScreenAdaptor: '${el}' is not exist`);
      return;
    }
    const style = document.createElement('style');
    const ignoreStyle = document.createElement('style');
    style.lang = 'text/css';
    ignoreStyle.lang = 'text/css';
    style.id = 'AutoScreenAdaptorStyle';
    ignoreStyle.id = 'ignoreStyle';
    style.innerHTML = `
      body {
        overflow: hidden;
      }
    `;
    dom.appendChild(style);
    dom.appendChild(ignoreStyle);
    dom.style.height = `${designHeight}px`;
    dom.style.width = `${designWidth}px`;
    dom.style.transformOrigin = '0 0';
    this.keepAdapt(designWidth, designHeight, dom, ignore);
    const runner = () => {
      if (dom) {
        this.keepAdapt(designWidth, designHeight, dom, ignore);
      }
      this.isElRectification && this.elRectification(this.currelRectification, this.currelRectificationLevel);
    };
    this.resizeListener = () => {
      // @ts-ignore
      clearTimeout(this.timer);
      if (delay !== 0) {
        this.timer = setTimeout(() => {
          runner();
        }, delay);
      } else {
        runner();
      }
    };
    // @ts-ignore
    resize && window.addEventListener('resize', this.resizeListener);
    this.isAutofitRunnig = true;
    setTimeout(() => {
      if (dom) {
        dom.style.transition = `${transition}s`;
      }
    });
  }

  /**
   * 关闭 AutoScreenAdaptor.js造成的影响
   * @param el
   */
  destroy (el = '#app') {
    try {
      this.isElRectification = false;
      // @ts-ignore
      window.removeEventListener('resize', this.resizeListener);
      const ss = document.querySelector('#AutoScreenAdaptorStyle');
      ss && ss.remove();
      // @ts-ignore
      document.querySelector(this.currRenderDom ? this.currRenderDom : el).style = '';
      const arr: HTMLElement[] = Array.from(document.querySelectorAll(this.currelRectification));
      for (const item of arr) {
        item.style.width = '';
        item.style.height = '';
        item.style.transform = '';
      }
    } catch (error) {
      console.error('AutoScreenAdaptor: Failed to remove normally', error);
      this.isAutofitRunnig = false;
    }
    this.isAutofitRunnig && console.log('%c AutoScreenAdaptor.js  is destroy', 'font-weight: bold;color: #707070; background: #c9c9c9; padding: 8px 12px; border-radius: 4px;');
  }

  /**
   * 检测浏览器是否支持 transform: scale
   */
  isScaleSupport (): boolean {
    let flag = false;
    const el = document.createElement('div');
    document.body.appendChild(el);
    el.style.cssText = 'width: 100px;height: 100px;position: fixed;left: 0;top: 0';
    el.style.transform = 'scale(2)';
    el.style.webkitTransform = 'scale(2)';
    flag = el.getBoundingClientRect().width === 200;
    el.remove();
    return flag;
  }

  /**
   * 获取浏览器支持的transform属性名称 为了js的兼容写法
   */
  getTransform (): string {
    let transform = '';
    // document.createElement() 创建元素节点，
    const divStyle: CSSStyleDeclaration = document.createElement('div').style; // 返回的是一组object对象，里面包含了transform
    // 可能涉及到的几种兼容性写法，通过循环，找出浏览器识别的那个
    // 依次为： 全部、Safari 和 Chrome、firefox、ie9、opera
    const transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'];
    for (let i = 0; i < transformArr.length; i++) {
      if (transformArr[i] in divStyle) {
        // 找到以后立刻返回，结束函数
        transform = transformArr[i];
        break;
      }
    }
    // 如果没有找到，就直接返回空字符串
    return transform;
  }

  /**
   * @description 设置缩放的兼容写法
   * @param dom {HTMLElement}
   * @param scale {number}
   */
  setScale (dom: HTMLElement, scale: number) {
    if (this.isSupportScale) {
      // @ts-ignore
      dom.style[this.getTransform()] = `scale(${scale})`;
    } else {
      // @ts-ignore
      dom.style.zoom = scale;
    }
  }

  /**
   * @description 设置指定容器自适应的处理
   * @param dw {number} 设计稿宽度
   * @param dh {number} 设计稿高度
   * @param dom {HTMLElement} 容器
   * @param ignore {IgnoreDom[]} 忽略不缩放的元素
   */
  keepAdapt (dw: number, dh: number, dom: HTMLElement, ignore: IgnoreDom[]) {
    const clientHeight = document.documentElement.clientHeight;
    const clientWidth = document.documentElement.clientWidth;
    this.currScale = (clientWidth / clientHeight < dw / dh) ? (clientWidth / dw) : (clientHeight / dh);
    dom.style.height = `${clientHeight / this.currScale}px`;
    dom.style.width = `${clientWidth / this.currScale}px`;
    this.setScale(dom, this.currScale);

    for (const item of ignore) {
      const itemEl = item.dom;
      if (!itemEl) {
        console.error(`AutoScreenAdaptor: bad selector: ${itemEl}`);
        continue;
      }
      const realScale = (item.scale ? item.scale : 1 / this.currScale);
      const realFontSize = realScale !== this.currScale ? `${item.fontSize}px` : 'inherit';
      const realWidth = realScale !== this.currScale ? `${item.width}px` : 'auto';
      const realHeight = realScale !== this.currScale ? `${item.height}px` : 'auto';
      const regex = new RegExp(`${itemEl}(\x20|{)`, 'gm');
      const d: HTMLElement | null = document.querySelector('#ignoreStyle'), s = d ? d.innerHTML : '';
      const isIgnored = regex.test(s);
      if (isIgnored) {
        continue;
      }
      if (d) {
        d.innerHTML += `\n${itemEl} {transform-origin: 0 0;width: ${realWidth}!important;height: ${realHeight}!important;}`;
        d.innerHTML += `\n${itemEl} div ,${itemEl} span,${itemEl} a,${itemEl} * {font-size: ${realFontSize};}`;
        this.setScale(d, realScale);
      }
    }
  }

  /**
   * 一些canvas渲染的图表也会有事件偏移，而图表不同于地图，当使用ignore时，过大的图表可能会显示不全，因此可以使用elRectification （性能上不如ignore）
   * 如果ignore无法满足需求，可以使用 AutoScreenAdaptor.elRectification(".classNameOrId")
   * 使用 elRectification 时，需要被矫正的元素已经挂载
   * @param el
   * @param level
   */
  elRectification (el: string, level = 1) {
    if (!this.isAutofitRunnig) {
      console.error('AutoScreenAdaptor.js：AutoScreenAdaptor has not been initialized yet');
    }
    !el && console.error(`autofit.js：bad selector: ${el}`);
    this.currelRectification = el;
    this.currelRectificationLevel = level;
    const currEl: HTMLElement[] = Array.from(document.querySelectorAll(el));
    if (currEl.length === 0) {
      console.error('AutoScreenAdaptor.js：elRectification found no element');
      return;
    }
    for (const item of currEl) {
      if (!this.isElRectification) {
        // @ts-ignore
        item.originalWidth = item.clientWidth;
        // @ts-ignore
        item.originalHeight = item.clientHeight;
      }
      const rectification = this.currScale === 1 ? 1 : this.currScale * level;
      // @ts-ignore
      item.style.width = `${item.originalWidth * rectification}px`;
      // @ts-ignore
      item.style.height = `${item.originalHeight * rectification}px`;
      item.style.transform = `scale(${1 / this.currScale})`;
      item.style.transformOrigin = '0 0';
    }
    this.isElRectification = true;
  }
}

